// @Author EthanScriptOn
// @Desc
package config_source_loader

import (
	"errors"
	"fmt"
	"gitee.com/fatzeng/srf_switch_config/config_node"
	"regexp"
	"strconv"
)

type FileBaseSourceLoaderSteps struct {
	ConfigSourceLoaderSteps
}

func GenerateFileBaseSourceLoaderSteps(steps ConfigSourceLoaderSteps) (*FileBaseSourceLoaderSteps, error) {
	if steps == nil {
		return nil, errors.New("steps can not be nil")
	}
	fileBaseSourceLoaderSteps := new(FileBaseSourceLoaderSteps)
	fileBaseSourceLoaderSteps.ConfigSourceLoaderSteps = steps
	return fileBaseSourceLoaderSteps, nil
}

func (f *FileBaseSourceLoaderSteps) ConvertLineDescribe(lines []string) ([]*SegmentLineDescribe, error) {
	panic(errors.New("this function needs to be rewritten"))
}

func (f *FileBaseSourceLoaderSteps) ConvertSegmentToMap(sourceLoaderNode *config_node.SourceLoaderNode) (map[string]interface{}, error) {
	content, err := f.getContent(sourceLoaderNode)
	if err != nil {
		return nil, err
	}
	if len(content) <= 0 {
		return nil, errors.New("sourceLoaderNode content is empty")
	}
	sourceLoaderNodeMap := make(map[string]interface{})
	usedMap := sourceLoaderNodeMap
	for _, lineDescribe := range content {
		length := len(lineDescribe.multiKeySlice)
		for index, keyDescribe := range lineDescribe.multiKeySlice {
			var usedMapTemp = make(map[string]interface{})
			if keyDescribe.isSLice {
				if index == length-1 {
					usedSlice := make([]interface{}, 0)
					usedSlice = append(usedSlice, lineDescribe.content)
					usedMap[keyDescribe.key] = usedSlice
					usedMap = sourceLoaderNodeMap
					continue
				}
				usedMapSliceTemp := make([]map[string]interface{}, 0)
				usedMapSliceTemp = append(usedMapSliceTemp, usedMapTemp)
				usedMap[keyDescribe.key] = usedMapSliceTemp
			} else {
				if index == length-1 {
					usedMap[keyDescribe.key] = lineDescribe.content
					usedMap = sourceLoaderNodeMap
					continue
				}
				usedMap[keyDescribe.key] = usedMapTemp
			}
			usedMap = usedMapTemp
		}
	}
	return sourceLoaderNodeMap, nil
}

func (f *FileBaseSourceLoaderSteps) SplitSegment(lineDescribes []*SegmentLineDescribe) ([]Segment, error) {
	panic(errors.New("this function needs to be rewritten"))
}

func (f *FileBaseSourceLoaderSteps) IsAnnotation(line string) bool {
	panic(errors.New("this function needs to be rewritten"))
}

func (f *FileBaseSourceLoaderSteps) getContent(sourceLoaderNode *config_node.SourceLoaderNode) ([]*SegmentLineDescribe, error) {
	if sourceLoaderNode == nil {
		return nil, errors.New("sourceLoaderNode is nil")
	}
	content := sourceLoaderNode.GetContent()
	contentLineDescribe, ok := content.([]*SegmentLineDescribe)
	if !ok {
		return nil, errors.New("sourceLoaderNode content is not []*SegmentLineDescribe")
	}
	return contentLineDescribe, nil
}

type AllowableCutFileBaseSourceLoaderSteps struct {
	*FileBaseSourceLoaderSteps
	keyValuePairsSlicing *SourceLoaderSlicing
	multiKeySlicing      *SourceLoaderSlicing
	indexSurround        *regexp.Regexp
}

func GenerateAllowableCutFileBaseSourceLoaderSteps(steps ConfigSourceLoaderSteps, keyValuePairsSlicing, multiKeySlicing *SourceLoaderSlicing, indexSurround string) (*AllowableCutFileBaseSourceLoaderSteps, error) {
	allowableCutFileBaseSourceLoaderSteps := new(AllowableCutFileBaseSourceLoaderSteps)
	loaderSteps, err := GenerateFileBaseSourceLoaderSteps(steps)
	if err != nil {
		return nil, err
	}
	if keyValuePairsSlicing == nil || multiKeySlicing == nil {
		return nil, errors.New("need to config_define cutting rules,for example,how to split key values,for example,how to cut multiple keys")
	}
	allowableCutFileBaseSourceLoaderSteps.keyValuePairsSlicing = keyValuePairsSlicing
	allowableCutFileBaseSourceLoaderSteps.multiKeySlicing = multiKeySlicing
	allowableCutFileBaseSourceLoaderSteps.FileBaseSourceLoaderSteps = loaderSteps
	if indexSurround == "" {
		return nil, errors.New("need to config_define how to obtain multi key index rules")
	}
	allowableCutFileBaseSourceLoaderSteps.indexSurround = regexp.MustCompile(indexSurround)
	return allowableCutFileBaseSourceLoaderSteps, nil
}

func (a *AllowableCutFileBaseSourceLoaderSteps) ConvertLineDescribe(lines []string) ([]*SegmentLineDescribe, error) {
	describes := make([]*SegmentLineDescribe, 0)
	for _, line := range lines {
		if a.ConfigSourceLoaderSteps.IsAnnotation(line) {
			continue
		}
		describe, err := a.GenerateLineDescribe(line)
		if err != nil {
			return nil, err
		}
		describes = append(describes, describe)
	}
	return describes, nil
}

func (a *AllowableCutFileBaseSourceLoaderSteps) GenerateLineDescribe(line string) (*SegmentLineDescribe, error) {
	segmentLineDescribe := &SegmentLineDescribe{
		multiKeySlice: make([]*SegmentLineKeyDescribe, 0),
	}
	segmentLineDescribe.originalContent = line
	if !a.keyValuePairsSlicing.Requirements(line) {
		return segmentLineDescribe, nil
	}
	lineKey, err := a.keyValuePairsSlicing.GetFirst(line)
	if err != nil {
		return nil, err
	}
	lineKeyDescribe, err := a.buildSegmentLineKeyDescribe(lineKey)
	if err != nil {
		return nil, err
	}
	for _, describePtr := range lineKeyDescribe {
		describe := describePtr
		segmentLineDescribe.multiKeySlice = append(segmentLineDescribe.multiKeySlice, describe)
	}
	lineContent, err := a.keyValuePairsSlicing.GetLast(line)
	if err != nil {
		return nil, err
	}
	segmentLineDescribe.content = lineContent
	return segmentLineDescribe, nil
}

func (a *AllowableCutFileBaseSourceLoaderSteps) buildSegmentLineKeyDescribe(lineKey string) ([]*SegmentLineKeyDescribe, error) {
	multiKeyMsg, err := a.parseKey(lineKey)
	if err != nil {
		return nil, err
	}
	result := make([]*SegmentLineKeyDescribe, len(multiKeyMsg))
	level := 0
	for _, pair := range multiKeyMsg {
		keyName := pair.tag
		keyIndex := pair.idx
		singleKey := &SegmentLineKeyDescribe{
			key:     keyName,
			index:   keyIndex,
			level:   level,
			isSLice: keyIndex != -1,
		}
		result = append(result, singleKey)
		level++
	}
	return result, nil
}

func (a *AllowableCutFileBaseSourceLoaderSteps) parseKey(input string) ([]*TagIndexPair, error) {
	var result []*TagIndexPair
	parts := a.multiKeySlicing.Split(input)
	if len(parts) <= 0 {
		return nil, errors.New("lineKey is empty")
	}
	for _, part := range parts {
		matches := a.indexSurround.FindStringSubmatch(part)
		if matches != nil {
			idx, err := strconv.Atoi(matches[2])
			if err != nil {
				return nil, fmt.Errorf("the index of the key %v must be a number", part)
			}
			result = append(result, &TagIndexPair{tag: matches[1], idx: idx})
		} else {
			result = append(result, &TagIndexPair{tag: part, idx: -1})
		}
	}
	return result, nil
}
